package onlinebanking.security.threshold;

import java.util.Random;

import onlinebanking.math.Polynomial;
import onlinebanking.math.Rational;

public class ThresholdEngine {
	private int theshold;
	private int shares;
	private Random r = new Random();
	private int randomRange = 20;
	
	public Polynomial generateRandomPoly(int degree, int data){
//		System.out.println("Generating random polynomial with degree " +degree+ " and " +data+ " as constant...");
		
		Polynomial f = new Polynomial();
		if(degree < 0) return f;
		
		long coeffs[] = new long[degree + 1];
		
		coeffs[coeffs.length-1] = data;
		
		for(int i = 0 ;i < coeffs.length-1; i++){
			int c = r.nextInt(this.randomRange) - this.randomRange/2;
			if(i == 0){
				while( c == 0){
					c = r.nextInt(this.randomRange) - this.randomRange/2;	//ensures that the polynomial is in degree 
				}
			}
			coeffs[i] = c;
		}
		
		f.constructPoly(coeffs);
		f.printPoly();
		
		return f;
	}
	
	public Share[] generateShares(Polynomial f, int shareno[]){
		
		int totalshares = shareno.length;
	//	System.out.println("Generating " + totalshares+" shares...");
		
		
		Share shares[] = new Share[totalshares];
		
		for(int i = 0; i< totalshares;i++){
			int x = shareno[i];
			if(x == 0){
				System.out.println("Share no cannot be zero");
				return shares;
			}
			long y = f.evaluatePoly(x);
			
			shares[i] = new Share(x,y);
		}
				
		return shares;
	}
	
	public Polynomial LagrangeInterpolate(Share shares[]){
		
		//initialize polynomial
		Polynomial sigma = new Polynomial(0);
				
		//SIGMA Notation
		for(int i = 0 ; i<shares.length; i++){
									
			//initialize pi notation
			Polynomial pi = new Polynomial(1);
			
			//PI notation
			for(int j = 0; j< shares. length; j++){
				if(i == j)
					continue;
				
				// (1/xi-xj)x -(xj/xi-xj)
				Rational firstterm = new Rational( 1 , shares[i].getX()  - shares[j].getX() );
				Rational secondterm = new Rational( 0 - shares[j].getX() , shares[i].getX()  - shares[j].getX() );
				Rational terms[] = {firstterm,secondterm};				
				
				Polynomial pi_iterate = new Polynomial(terms);
				pi = pi.multiply(pi_iterate);			
				
			}//end of j-loop
			
			//multiply yi
			long yi[] = { shares[i].getY() };
			Polynomial sigma_iterate = pi.multiply(new Polynomial(yi));	
			
			sigma = sigma.add(sigma_iterate);
			
		}//end of i-loop
		
		return sigma;
		
	}
	
	public long recoverData(Share shares[]){
		Polynomial f = this.LagrangeInterpolate(shares);
		return f.evaluatePoly(0);
	}
	
	public static void main(String[] args) {
		ThresholdEngine te = new ThresholdEngine();
		int threshold = 2;
		int tac = 70;
		int members = 3;
		
		System.out.println("Threshold: "+ threshold +" TAC: "+tac);
		
		Polynomial f = te.generateRandomPoly(threshold -1, tac);
		
//		Rational r1 = new Rational(5);
//		Rational r2 = new Rational(3);
//		Rational r3 = new Rational(20);
//		
//		Rational arr[] = {r1,r2,r3};
//		Polynomial f = new Polynomial(arr);
//		f.printPoly();
		
		
		int sharenos[] = new int[members];
		for(int i = 1 ; i<=members; i++){
			sharenos[i-1] = i;
		}
		
		Share shares[] = te.generateShares(f, sharenos);
		
		for(int i= 0; i<sharenos.length; i++){
			System.out.print("( "+shares[i].getX()+" , "+shares[i].getY()+" )");
		}
		System.out.println("");
		System.out.println("Interpolating...");
		Share gshare[] = new Share[threshold];
		for (int i = 0; i < gshare.length; i++) {
			gshare[i] = shares[i];
			System.out.print("( "+shares[i].getX()+" , "+shares[i].getY()+" )");
		}
		
		
		Polynomial reconstruct = te.LagrangeInterpolate(gshare);
		System.out.println("Reconstructed:");
		reconstruct.printPoly();
	}
	
}
